package com.algo.tools.dataset_stats;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;

/**
 * This class is for generating statistics about a sequence database
 * 
 * Copyright (c) 2008-2012 Philippe Fournier-Viger
 * 
 * This file is part of the SPMF DATA MINING SOFTWARE
 * (http://www.philippe-fournier-viger.com/spmf).
 * 
 * SPMF is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * SPMF is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * SPMF. If not, see <http://www.gnu.org/licenses/>.
 */
public class SequenceStatsGenerator {

	private List<Sequence> sequences = new ArrayList<Sequence>();
	int maxItem = 0;
	String input;

	public void getStats(String path) throws IOException {

		String thisLine;
		String input = path;
		BufferedReader myInput = null;
		try {
			FileInputStream fin = new FileInputStream(new File(path));
			myInput = new BufferedReader(new InputStreamReader(fin));
			while ((thisLine = myInput.readLine()) != null) {
				String entiers[] = thisLine.split(" ");
				Sequence sequence = new Sequence();
				Itemset itemset = new Itemset();
				for (String entier : entiers) {
					if (entier.codePointAt(0) == '<') { // Timestamp
					} else if (entier.equals("-1")) { // itemset separator
						sequence.addItemset(itemset);
						itemset = new Itemset();
					} else if (entier.equals("-2")) { // indicates end of
														// sequence
						sequences.add(sequence);
					} else {
						Integer item = Integer.parseInt(entier);
						if (item >= maxItem) {
							maxItem = item;
						}
						itemset.addItem(item);
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (myInput != null) {
				myInput.close();
			}
		}

		System.out.println("============  SEQUENCE DATABASE STATS ==========");
		System.out.println("Number of sequences : " + sequences.size());
		// average size of sequence
		java.util.Set<Integer> items = new java.util.HashSet<Integer>();
		List<Integer> sizes = new ArrayList<Integer>();
		List<Integer> itemsetsizes = new ArrayList<Integer>();
		List<Integer> differentitems = new ArrayList<Integer>();
		List<Integer> appearXtimesbySequence = new ArrayList<Integer>();
		for (Sequence sequence : sequences) {
			sizes.add(sequence.size());
			HashMap<Integer, Integer> mapIntegers = new HashMap<Integer, Integer>();
			for (Itemset itemset : sequence.getItemsets()) {
				itemsetsizes.add(itemset.size());
				for (Integer item : itemset.getItems()) {
					Integer count = mapIntegers.get(item);
					if (count == null) {
						count = 0;
					}
					count = count + 1;
					mapIntegers.put(item, count);
					items.add(item);
				}
			}
			differentitems.add(mapIntegers.entrySet().size());

			for (Entry<Integer, Integer> entry : mapIntegers.entrySet()) {
				appearXtimesbySequence.add(entry.getValue());
			}
		}
		System.out.println("File " + input);
		System.out.println("Number of distinct items: " + items.size());
		System.out.println("Largest item id: " + maxItem);
		System.out.println("Average number of itemsets per sequence : "
				+ calculateMean(sizes) + " standard deviation: "
				+ calculateStdDeviation(sizes) + " variance: "
				+ calculateVariance(sizes));
		System.out.println("Average number of distinct item per sequence : "
				+ calculateMean(differentitems) + " standard deviation: "
				+ calculateStdDeviation(differentitems) + " variance: "
				+ calculateVariance(differentitems));
		System.out
				.println("Average number of occurences in a sequence for each item appearing in a sequence : "
						+ calculateMean(appearXtimesbySequence)
						+ " standard deviation: "
						+ calculateStdDeviation(appearXtimesbySequence)
						+ " variance: "
						+ calculateVariance(appearXtimesbySequence));
		System.out.println("Average number of items per itemset : "
				+ calculateMean(itemsetsizes) + " standard deviation: "
				+ calculateStdDeviation(itemsetsizes) + " variance: "
				+ calculateVariance(itemsetsizes));
		
		sequences = null;
	}


	private static double calculateMean(List<Integer> list) {
		double sum = 0;
		for (Integer val : list) {
			sum += val;
		}
		return sum / list.size();
	}

	private static double calculateStdDeviation(List<Integer> list) {
		double deviation = 0;
		double mean = calculateMean(list);
		for (Integer val : list) {
			deviation += Math.pow(mean - val, 2);
		}
		return Math.sqrt(deviation / list.size());
	}

	private static double calculateMeanD(List<Double> list) {
		double sum = 0;
		for (Double val : list) {
			sum += val;
		}
		return sum / list.size();
	}

	private static double calculateStdDeviationD(List<Double> list) {
		double deviation = 0;
		double mean = calculateMeanD(list);
		for (Double val : list) {
			deviation += Math.pow(mean - val, 2);
		}
		return Math.sqrt(deviation / list.size());
	}

	private static double calculateVariance(List<Integer> list) {
		double deviation = 0;
		double mean = calculateMean(list);
		for (Integer val : list) {
			deviation += Math.pow(mean - val, 2);
		}
		return Math.pow(Math.sqrt(deviation / list.size()), 2);
	}

	private static double calculateMinValue(List<Integer> list) {
		int min = 99999;
		for (Integer val : list) {
			if (val <= min) {
				min = val;
			}
		}
		return min;
	}

	private static double calculateMaxValue(List<Integer> list) {
		int max = 0;
		for (Integer val : list) {
			if (val >= max) {
				max = val;
			}
		}
		return max;
	}
}
